package com.CMUBiS.PageControllers;

import java.util.ArrayList;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;

import com.CMUBiS.Data.Error;
import com.CMUBiS.Data.GlobalErrors;
import com.CMUBiS.Data.GlobalVars;
import com.CMUBiS.util.Account;
import com.CMUBiS.util.Address;
import com.CMUBiS.util.CheckInputs;
import com.CMUBiS.util.StopWatch;

/**
 * This is a blank example of a page controller. You can copy and edit it to make new pages
 * 
 * @author Andy (andrewtu@cmu.edu, turner.andy@gmail.com)
 * @version 0.1
 * @since 0.1
 * @date 04/05/2011
 */

public class BuyItemController extends PageController{
	// Variables passed in the request
	private int userID = -1;
	private String authToken = null;
	private int itemID;
	private int quantity;
	
	private Address address;
	private int addressID = -1;
	private String street = null;
	private String town = null;
	private String zip = null;
	private String state = null;
	
	private Account account;
	private int accountID = -1;
	private String nameOnCard;
	private String creditCardNo;
	private String cvv;
	private String expirationDateString;
	private Date expirationDate;
	//private Boolean cardIsValid;
	
	// Structures to hold the DB data
	private ArrayList<String[]> states = null;
	
	// Structures to hold the parsed page data
	private String[] stateSelections = null;
	private String redirectURL = null;

	/**
	 * This method checks the page for any input errors that may have come from Client generator error
	 * These would need to be check in real life to stop users attempting to hack and mess with things
	 * 
	 * @param request
	 * @author Andy (andrewtu@cmu.edu, turner.andy@gmail.com)
	 */
	public void checkInputs(HttpServletRequest request){
		StopWatch timer;
		if(GlobalVars.collectStats){
			timer = new StopWatch();
			timer.start();
		}
		
		if(request != null){
			super.checkInputs(request);
			
			// Get the userID (if exists), we will pass it along to the next pages
			try{
				this.userID = CheckInputs.checkUserID(request);
			}
			catch(Error e){
				if(!errors.contains(e))
					errors.add(e);
			}
			
			// Get the authToken (if exists), we will pass it along to the next pages
			try{
				this.authToken = CheckInputs.checkAuthToken(request);
			}
			catch(Error e){
				if(!errors.contains(e))
					errors.add(e);
			}
			
			// Get the itemID 
			try{
				this.itemID = CheckInputs.checkItemID(request);
			}
			catch(Error e){
				if(!errors.contains(e))
					errors.add(e);
				
				this.itemID = -1;
			}
			
			// Get the quantity
			try{
				this.quantity = CheckInputs.checkQuantity(request);
			}
			catch(Error e){
				if(!errors.contains(e))
					errors.add(e);
				
				this.quantity = -1;
			}
			
			/*
			 * Get the account information for the payment
			 */
			// Get the accountID
			try{
				this.accountID = CheckInputs.checkAccountID(request);
			}
			catch(Error e){
				// We don't care if the accountID is wrong as the user doesn't have to save the account
				this.accountID = -1;
			}
			
			// Get the name on the card
			this.nameOnCard = CheckInputs.getParameter(request, "nameOnCard");
			
			// Get the credit card number
			this.creditCardNo = CheckInputs.getParameter(request, "creditCardNo");
			
			// Get the cvv code
			this.cvv = CheckInputs.getParameter(request, "cvv");
			
			// Get the expiration date
			this.expirationDateString = CheckInputs.getParameter(request, "expirationDate");
			try{
				this.expirationDate = CheckInputs.checkExpirationDate(request);
			}
			catch(Error e){
				if(!errors.contains(e))
					errors.add(e);
			}
			
			/*
			 * Get the address information for the payment
			 */
			// Get the AddressID
			try{
				this.addressID = CheckInputs.checkAddressID(request);
			}
			catch(Error e){
				// The user must have a default address, we'll read it later
				this.addressID = -1;
			}
			
			// Get the street
			this.street = CheckInputs.getParameter(request, "street");
			
			if(street.equals(EMPTY)){
				if(!errors.contains(GlobalErrors.streetEmpty))
					errors.add(GlobalErrors.streetEmpty);
			}
			
			// Get the town
			this.town = CheckInputs.getParameter(request, "town");
			
			if(town.equals(EMPTY)){
				if(!errors.contains(GlobalErrors.townEmpty))
					errors.add(GlobalErrors.townEmpty);
			}
			
			// Get the zip
			try{
				this.zip = CheckInputs.checkZip(request);
			}
			catch(Error e){
				if(!errors.contains(e))
					errors.add(e);
				
				// As the zip was not correct we'll fill the box with the user's old, bad data
				this.zip = CheckInputs.getParameter(request, "zip");
			}
			
			// Check the state
			try{
				this.state = CheckInputs.checkState(request);
			}
			catch(Error e){
				if(!errors.contains(e))
					errors.add(e);
				
				// As the state was not correct set a default
				this.state = "0";
			}
		}
		
		// Calculate how long that took
		if(GlobalVars.collectStats){
			timer.stop();
			this.totalGetParamTime += timer.getTimeTaken();
		}
	}

	/**
	 * This method get the data needed for the HTML4 page from the database
	 * 
	 * @author Andy (andrewtu@cmu.edu, turner.andy@gmail.com)
	 */
	public void getHTML4Data() {	
		StopWatch timer;
		if(GlobalVars.collectStats){
			timer = new StopWatch();
			timer.start();
		}
		
		// Get the list of states from the data base
		try{
			this.states = GlobalVars.db.getStates();
		}
		catch(Exception e){
			errors.add(new Error("BuyItemControl: getHTML4data: could not read the states from the database", e));
		}
		
		// If there is an accountID we'll read that account, otherwise we'll get the default account
		if(this.accountID>0){
			this.account = GlobalVars.db.getAccount(accountID);
		}
		else{
			//TODO: get default account
		}
		
		// If there is an address, we'll use it, otherwise will use the default address
		if(this.addressID>0){
			this.address = GlobalVars.db.getAddress(addressID);
		}
		else{
			this.address = GlobalVars.db.getDefaultAddress(this.userID);
		}
		
		// Calculate how long that took
		if(GlobalVars.collectStats){
			timer.stop();
			this.totalDBtime += timer.getTimeTaken();
		}
	}

	/**
	 * This method processes all of the data that was read from the database such that it is ready to be printed
	 * on to the page. We try to do as much of the page logic here as possible
	 * 
	 * @author Andy (andrewtu@cmu.edu, turner.andy@gmail.com)
	 */
	public void processHTML4() {
		StopWatch timer;
		if(GlobalVars.collectStats){
			timer = new StopWatch();
			timer.start();
		}
		
		// Set the page's account information
		if(this.account != null){
			this.accountID = this.account.getAccountID();
			this.nameOnCard = this.account.getNameOnCard();
			this.creditCardNo = this.account.getCreditCardNo();
			this.cvv = this.account.getCVV();
			this.expirationDate = this.account.getExpirationDate();
		}
		
		// Set the page's address information
		if(this.address != null){
			this.addressID = address.getId();
			this.street = address.getStreet();
			this.town = address.getTown();
			this.zip = address.getZip();
			this.state = Integer.toString(address.getState());
		}
		
		/*
		 * Make the state selections
		 */
		int stateNo;
		// If the state is a number and there are states read from the database we can make the list
		try{
			stateNo = Integer.parseInt(this.state);
			
			if(this.states != null){
				this.stateSelections = new String[this.states.size()+1];
				
				// Add the 'please select'
				this.stateSelections[0] = "<option value=\"0\""+ (stateNo==0?" SELECTED":"") + ">Please Select...</option>";
				
				// Add the other states
				int length = this.states.size();
				
				for(int i=0; i<length; i++){
					String[] state = this.states.get(i);
					this.stateSelections[i+1] = "<option value=\"" + state[0] + "\"" + (this.state.equals(state[0])?" SELECTED":"") + ">" + state[1] + " - " + state[2] + "</option>";
				}
			}
			else{
				this.stateSelections = new String[1];
				this.stateSelections[0] = "<option value=\"0\""+ (stateNo==0?" SELECTED":"") + ">Please Select...</option>";
			}
		}
		catch(Exception e){
			if(!errors.contains(GlobalErrors.stateNotAnInteger))
				errors.add(GlobalErrors.stateNotAnInteger);
		}
		
		// Calculate how long that took
		if(GlobalVars.collectStats){
			timer.stop();
			this.totalProcessingTime += timer.getTimeTaken();
		}
	}
	
	/**
	 * Gets the HTML5 data from the database
	 * 
	 * @author Andy (andrewtu@cmu.edu, turner.andy@gmail.com)
	 */
	public void getHTML5Data(){
		StopWatch timer;
		if(GlobalVars.collectStats){
			timer = new StopWatch();
			timer.start();
		}
		
		// Calculate how long that took
		if(GlobalVars.collectStats){
			timer.stop();
			this.totalDBtime += timer.getTimeTaken();
		}
	}
	
	/**
	 * Processes the HTML5 data that is needed to create the page
	 * 
	 * @author Andy (andrewtu@cmu.edu, turner.andy@gmail.com)
	 */
	public void processHTML5(){
		StopWatch timer;
		if(GlobalVars.collectStats){
			timer = new StopWatch();
			timer.start();
		}
		
		// Calculate how long that took
		if(GlobalVars.collectStats){
			timer.stop();
			this.totalProcessingTime += timer.getTimeTaken();
		}
	}
	
	private Boolean verifyCardNumber(){
		// WOO!! it just wouldn't be made by students if we didn't copy from wikipedia!!
		// TODO: Andy: Check this algorithm is correct
		// ref: http://en.wikipedia.org/wiki/Luhn_algorithm
		Boolean correct = Boolean.FALSE;
		final int[][] sumTable = {{0,1,2,3,4,5,6,7,8,9},{0,2,4,6,8,1,3,5,7,9}};
		
	    int sum = 0, flip = 0;
	 
	    try{
		    for (int i = this.creditCardNo.length() - 1; i >= 0; i--) {
		      sum += sumTable[flip++ & 0x1][Character.digit(this.creditCardNo.charAt(i), 10)];
		    }
		    
		    if(!(sum % 10 == 0)){
		    	if(!errors.contains(GlobalErrors.creditCardNoInvalid))
		    		errors.add(GlobalErrors.creditCardNoInvalid);
		    }
		    else
		    	correct = Boolean.TRUE;
	    }
	    catch(Exception e){
	    	if(!errors.contains(GlobalErrors.creditCardNoInvalid))
				errors.add(GlobalErrors.creditCardNoInvalid);
	    }
	    
		return correct;
	}
	
	public boolean buyNow(){
		if(this.errors.size() == 0)
			if(GlobalVars.db.checkAuthToken(this.userID, this.authToken)){
				// Check that the credit card number is correct
				if(this.verifyCardNumber()){
				
				
					// If the account number is present, get the account from the database, otherwise this is a temporary account
					Account account;
					if(this.accountID > 0)
						account = GlobalVars.db.getAccount(this.accountID);
					else{
						// otherwise it's a temporary account
						account = new Account(-1, null, this.nameOnCard, this.creditCardNo, this.cvv, this.expirationDate);
					}
					
					// If the addressID is present, get the address, otherwise it is a temporary address
					//TODO: either make the address everytime or lock the fields on the page
					Address address;
					if(this.addressID > 0)
						address = GlobalVars.db.getAddress(addressID);
					else{
						//TODO: new address based on the info passed
						address = null;
						System.out.println("The addressID is " + this.addressID);
					}
					
					// Buy the item
					boolean retVal2 = GlobalVars.db.buyItemNow(this.userID, this.itemID, this.quantity, account, address);
						
					if(retVal2) this.createRedirectURL();
						
					return retVal2;
				}
				else if(!errors.contains(GlobalErrors.creditCardNoInvalid))
					errors.add(GlobalErrors.creditCardNoInvalid);
			}
			else if(!errors.contains(GlobalErrors.incorrectAuthToken))
					errors.add(GlobalErrors.incorrectAuthToken);
		
		return false;
	}
	
	public String getRedirectURL(){
		return this.redirectURL;
	}
	
	/**
	 * If we successfully buy the item then we'll need to forward them on to the next page.
	 * We'll create the URL here
	 * 
	 * @author Andy (andrewtu@cmu.edu, turner.andy@gmail.com)
	 */
	private void createRedirectURL(){
		this.redirectURL = "./confirmbuy?userID=" + this.userID + "&authToken=" + this.authToken;
	}
	
	/**
	 * Returns the current userID as a String
	 * 
	 * @return String the userID
	 * @author Andy (andrewtu@cmu.edu, turner.andy@gmail.com)
	 */
	public String getUserIDString(){
		return Integer.toString(this.userID);
	}
	
	/**
	 * Returns the authToken sent to the page
	 * 
	 * @return string the authToken
	 * @author Andy (andrewtu@cmu.edu, turner.andy@gmail.com)
	 */
	public String getAuthTokenString(){
		return this.authToken;
	}
	
	/**
	 * Returns the street sent to the page
	 * 
	 * @return String the street sent to the page
	 * @author Andy (andrewtu@cmu.edu, turner.andy@gmail.com)
	 */
	public String getStreet(){
		return this.street;
	}
	
	/**
	 * Returns the town sent to the page
	 * 
	 * @return String the town sent to the page
	 * @author Andy (andrewtu@cmu.edu, turner.andy@gmail.com)
	 */
	public String getTown(){
		return this.town;
	}
	
	/**
	 * Returns the zip sent to the page
	 * 
	 * @return String the zip sent to the page
	 * @author Andy (andrewtu@cmu.edu, turner.andy@gmail.com)
	 */
	public String getZip(){
		return this.zip;
	}
	
	public int getItemID(){
		return this.itemID;
	}
	
	/**
	 * Returns the states that can be selected by the user
	 * 
	 * @return Sting[] a list of states (in option list format) that the user can choose from
	 * @author Andy (andrewtu@cmu.edu, turner.andy@gmail.com)
	 */
	public String[] getStateSelections(){
		return this.stateSelections;
	}
	
	public String getNameOnCard(){
		return this.nameOnCard;
	}
	
	public String getCreditCardNo(){
		return this.creditCardNo;
	}
	
	public String getCVV(){
		return this.cvv;
	}
	
	public int getQuantity(){
		return this.quantity;
	}
	
	public int getAddressID(){
		return this.addressID;
	}
	
	public int getAccountID(){
		return this.accountID;
	}
	
	public String getExpirationDateString(){
		return this.expirationDateString;
	}
}
